package com.ruoyi.system.controller;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.system.domain.FileModel;
import com.ruoyi.system.domain.dto.FileDto;
import com.ruoyi.system.domain.dto.FileInfoDto;
import com.ruoyi.system.domain.dto.FileListDto;
import com.ruoyi.system.domain.dto.IdsDto;
import com.ruoyi.system.service.FileLibraryService;
import com.ruoyi.system.service.FileService;
import com.ruoyi.system.service.ISysConfigService;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/file")
public class FileController extends BaseController {
    @Autowired
    private FileService fileService;
    @Autowired
    private FileLibraryService fileLibraryService;
    @Autowired
    private ISysConfigService configService;

    @PostMapping("/list")
    public R list(@RequestBody FileDto queryDto) throws UnsupportedEncodingException {
        //非公共文档库确认文档库所属
        if (queryDto.getLibraryId() != 1 && !Objects.equals(fileLibraryService.getById(queryDto.getLibraryId()).getUserId(), SecurityUtils.getUserId())) {
            throw new ServiceException("对不起，您没有权限进行此操作");
        }
        if (queryDto.getPage() == null || queryDto.getPage() == 0) {
            queryDto.setPage(1L);
        }
        if (queryDto.getSize() == null || queryDto.getSize() == 0) {
            queryDto.setSize(10L);
        }
        Page<FileModel> page = new Page<>(queryDto.getPage(), queryDto.getSize());
        LambdaQueryWrapper<FileModel> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FileModel::getIsDeleted, 0);
        queryWrapper.eq(FileModel::getLibraryId,queryDto.getLibraryId());
        queryWrapper.eq(FileModel::getUploadStatus,1).or().eq(FileModel::getAnalysisStatus,1).or().eq(FileModel::getAnalysisStatus,2);
        List<FileModel> fileModelList = fileService.list(queryWrapper);
        String libraryName = fileLibraryService.getById(queryDto.getLibraryId()).getKbName();
        if(!fileModelList.isEmpty()) {
            List<FileInfoDto> fileInfos = getFileStatus(libraryName);
            //查看大模型侧文档状态并更新数据库，在请求响应中增加文档下载URL
            for (FileModel fileModel : fileModelList) {
//                if (fileModel.getUploadStatus() == 1 || fileModel.getAnalysisStatus() == 1 || fileModel.getAnalysisStatus() == 2) {
                for (FileInfoDto fileInfo : fileInfos) {
                    if (fileInfo.getFileName().equals(fileModel.getKbName())) {
                        if (fileInfo.isInFolder()) {
                            fileModel.setUploadStatus(0);
                        } else {
                            fileModel.setUploadStatus(2);
                        }
                        if (fileInfo.isInDB()) {
                            fileModel.setAnalysisStatus(0);
                        } else {
                            fileModel.setAnalysisStatus(2);
                        }
                    }
                }
                fileService.updateById(fileModel);
//                }
            }
        }
        queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(FileModel::getFileName, queryDto.getFileName());
        queryWrapper.apply("file_name LIKE {0}", "%" + queryDto.getExtendName());
        queryWrapper.eq(FileModel::getIsDeleted, 0);
        queryWrapper.eq(FileModel::getLibraryId,queryDto.getLibraryId());
        if (queryDto.getAnalysisStatus()!=3) {
            queryWrapper.eq(FileModel::getAnalysisStatus, queryDto.getAnalysisStatus());
        }
        if (queryDto.getUploadStatus()!=3){
            queryWrapper.eq(FileModel::getUploadStatus,queryDto.getUploadStatus());
        }
        Page<FileModel> pageResult = fileService.page(page, queryWrapper);
        logger.debug("查询结果："+pageResult.getRecords());
        return R.ok(pageResult);
    }

    @PostMapping("/update")
    public R update(@RequestBody FileModel fileModel) {
        //非公共文档库确认文档库所属
        if (fileModel.getLibraryId() != 1 && !Objects.equals(fileModel.getUserId(), SecurityUtils.getUserId())) {
            throw new ServiceException("对不起，您没有权限进行此操作");
        }
        if (fileModel.getId() == null) {
            return R.fail("必须提供文档id");
        }
        checkPublicPermission(fileModel.getLibraryId());
        boolean res = fileService.updateById(fileModel);
        if (res) {
            return R.ok(null, "修改成功！");
        } else {
            return R.fail("修改失败！");
        }
    }

    @PostMapping("/delete")
    public R delete(@RequestBody IdsDto idsDto) {
        String baseURL = configService.selectConfigByKey("sys.m36b.address");
        List<Long> ids = idsDto.getIds();
        if (ids == null) {
            return R.fail("必须提供文档id");
        }
        //非管理员不能删除别人的文档，管理员也只能在删除别人文档库的时候删除库里的文档
        if (!SecurityUtils.getRoles().contains("超级管理员")&& !Objects.equals(fileService.getById(ids.get(0)).getUserId(), SecurityUtils.getUserId())){
            return R.fail("这不是您的文档库");
        }
        List<FileModel> fileModelList1 = new ArrayList<>();
        String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        JSONArray jsonArray = new JSONArray();
        for (Long id : ids) {
            if (id == null) {
                return R.fail("必须提供文档ID");
            }
            FileModel fileModel = new FileModel();
            fileModel.setId(id);
            fileModel.setGmtModified(time);
            fileModel.setIsDeleted(1);
            checkPublicPermission(fileService.getById(id).getLibraryId());
            fileModelList1.add(fileModel);
            jsonArray.add(fileService.getById(id).getKbName());
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("knowledge_base_name", fileLibraryService.getById(fileService.getById(ids.get(0)).getLibraryId()).getKbName());
        jsonObject.put("file_names", jsonArray);
        jsonObject.put("delete_content", true);

        if (post2Remote(baseURL + "/knowledge_base/delete_docs", jsonObject)) {
            boolean res = fileService.updateBatchById(fileModelList1);
            if (res) {
                return R.ok(null, "删除成功！");
            } else {
                logger.error("远程已删除，本地删除失败："+ids);
                return R.fail("数据库删除失败！");
            }
        }
        return R.fail("删除失败！");
    }

    @PostMapping("/upload")
    public R upload( FileListDto fileListDto) throws IOException {
        String baseURL = configService.selectConfigByKey("sys.m36b.address");
        List<String> fileTypes = Arrays.asList(configService.selectConfigByKey("sys.m36b.fileType").split(","));
        List<MultipartFile> files = fileListDto.getFiles();
        Long libraryId = fileListDto.getLibraryId();
        String introduction = fileListDto.getIntroduction();
        if (libraryId != 1 && !Objects.equals(fileLibraryService.getById(libraryId).getUserId(), SecurityUtils.getUserId())) {
            throw new ServiceException("对不起，您没有权限进行此操作");
        }
        if (fileListDto.getFiles() == null) {
            return R.fail("请上传文件");
        }
        List<String> kbNames = new ArrayList<>();
        List<FileModel> fileModelList1 = new ArrayList<>();
        List<String> failedFileList = new ArrayList<>();
        String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        String kbLibName = fileLibraryService.getById(libraryId).getKbName();
        for (MultipartFile multipartFile : files) {
            String originalName = multipartFile.getOriginalFilename();
            String uuid = UUID.randomUUID().toString();
            String extendName = null;
            if (originalName != null) {
                extendName = originalName.substring(originalName.lastIndexOf("."));
            }
            if (!fileTypes.contains(extendName)) {
                failedFileList.add(originalName);
                kbNames.add("");
                continue;
            }
            String kbName = uuid + extendName;
            FileModel fileModel = new FileModel();
            fileModel.setFileName(multipartFile.getOriginalFilename());
            fileModel.setLibraryId(libraryId);
            fileModel.setUserId(SecurityUtils.getUserId());
            fileModel.setCreator(SecurityUtils.getUserId());
            fileModel.setGmtCreated(time);
            fileModel.setIsDeleted(0);
            fileModel.setIntroduction(introduction);
            fileModel.setKbName(kbName);
            fileModel.setUploadStatus(1);
            fileModel.setAnalysisStatus(1);
            fileModel.setUrl(baseURL + "/knowledge_base/download_doc?knowledge_base_name=" + URLEncoder.encode(kbLibName
                    , "UTF-8") + "&file_name=" + URLEncoder.encode(fileModel.getKbName(), "UTF-8"));
            fileModelList1.add(fileModel);
            kbNames.add(kbName);
        }
        post2Remote(files, fileLibraryService.getById(libraryId).getKbName(), kbNames);
        //因远程端响应大多超时，所以忽略远程返回结果，直接响应请求
        boolean res = fileService.saveBatch(fileModelList1);
        if (res) {
            String msg = "上传成功";
            if (!failedFileList.isEmpty()) {
                msg = "部分文件上传失败，请检查文件类型：" + String.join(",", failedFileList);
            }
            logger.info(msg);
            return R.ok(null, msg);
        } else {
            return R.fail("上传失败！");
        }
    }

    @GetMapping("/download")
    public ResponseEntity download(Long id) throws IOException {
        String baseURL = configService.selectConfigByKey("sys.m36b.address");
        FileModel fileModel =fileService.getById(id);
        if(fileModel ==null){
            return new ResponseEntity<>("文档不存在！", HttpStatus.INTERNAL_SERVER_ERROR);
        }
        if(fileModel.getIsDeleted()==1){
            return new ResponseEntity<>("文档已删除！", HttpStatus.INTERNAL_SERVER_ERROR);
        }
        String kbLibName=fileLibraryService.getById(fileModel.getLibraryId()).getKbName();
        String url=baseURL + "/knowledge_base/download_doc?knowledge_base_name="+URLEncoder.encode(kbLibName
                , "UTF-8") + "&file_name=" + URLEncoder.encode(fileModel.getKbName(), "UTF-8");
        String destinationPath="/tmp/"+System.currentTimeMillis()+"/"+ fileModel.getFileName();
        if(downloadFile(url,destinationPath)){
            java.io.File file_ = new java.io.File(destinationPath);
            // 创建输入流
            InputStreamResource resource = new InputStreamResource(Files.newInputStream(file_.toPath()));
            // 设置响应头信息
            HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="+URLEncoder.encode(fileModel.getFileName(), "UTF-8")); // 指定文件名
            headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(file_.length())); // 文件大小
            headers.add(HttpHeaders.CONTENT_TYPE, org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE); // 文件类型
            // 返回响应实体
            return ResponseEntity.ok()
                    .headers(headers)
                    .contentLength(file_.length())
                    .contentType(org.springframework.http.MediaType.APPLICATION_OCTET_STREAM)
                    .body(resource);
        }
        return new ResponseEntity<>("下载失败！", HttpStatus.INTERNAL_SERVER_ERROR);
    }
    public boolean downloadFile(String urls, String destinationPath) {
        try {
            // 创建URL对象
            URL url = new URL(urls);
            // 打开连接
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            // 获取响应码
            int responseCode = connection.getResponseCode();
            // 检查是否成功获取文件
            if (responseCode == HttpURLConnection.HTTP_OK) {
                // 获取文件长度
                int fileLength = connection.getContentLength();
                // 创建输入流读取文件
                InputStream inputStream = connection.getInputStream();
                // 创建输出流写入文件
                Path parentDir = Paths.get(destinationPath).getParent();
                if (parentDir != null) {
                    Files.createDirectories(parentDir);
                }
                OutputStream outputStream = Files.newOutputStream(Paths.get(destinationPath));
                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
                // 关闭流
                outputStream.close();
                inputStream.close();
                return true;
            } else {
                System.out.println("Failed to download file: " + responseCode);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    private void checkPublicPermission(Long id) {
        if (id == 1) {
            if (!SecurityUtils.getRoles().contains("超级管理员")) {
                throw new ServiceException("对不起，您没有权限进行此操作");
            }
        }
    }

    private List<FileInfoDto> getFileStatus(String libraryName) throws UnsupportedEncodingException {
        String baseURL = configService.selectConfigByKey("sys.m36b.address");
        String res = HttpUtils.sendGet(baseURL + "/knowledge_base/list_files_with_details?knowledge_base_name=" + URLEncoder.encode(libraryName, "UTF-8"));
        JSONObject resp = JSON.parseObject(res);
        JSONArray data = resp.getJSONArray("data");
        List<FileInfoDto> fileInfos = data.toList(FileInfoDto.class);
        return fileInfos;
    }


    public void post2Remote(List<MultipartFile> files, String knowledgeBaseName, List<String> kbNames) throws IOException {
        /*
          将新增文档发给大模型
         */
        String baseURL = configService.selectConfigByKey("sys.m36b.address");
        List<String> kbNameList = new ArrayList<>();
        OkHttpClient client = new OkHttpClient().newBuilder().build();
        logger.info("上传到" + knowledgeBaseName + ":" + files.stream()
                .map(MultipartFile::getOriginalFilename)
                .collect(Collectors.toList()));
        MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM)
                .addFormDataPart("knowledge_base_name", knowledgeBaseName);
        String baseDir = "/tmp/";
        for (MultipartFile file : files) {
            String kbName = kbNames.get(files.indexOf(file));
            if (kbName.isEmpty()) {
                continue;
            }
            File dest = new File(baseDir);
            if(!dest.exists() && !dest.isDirectory()) {
                dest.mkdirs();}
            File file1=new File(baseDir,kbName);
            file.transferTo(file1 );
            builder.addFormDataPart("files", kbName,
                    okhttp3.RequestBody.create(MediaType.parse("application/octet-stream"),
                            file1));
            kbNameList.add(kbName);
        }
        Request request = new Request.Builder()
                .url(baseURL + "/knowledge_base/upload_docs")
                .method("POST", builder.build())
                .addHeader("Accept", "*/*")
                .addHeader("Connection", "keep-alive")
                .addHeader("Content-Type", "multipart/form-data; boundary=---93545")
                .build();
        try {
            Call call = client.newCall(request);
            call.enqueue(new Callback() {
                @Override
                public void onFailure(@NonNull Call call, @NonNull IOException e) {
                    logger.debug(e.getMessage());
                    if (!(e.getMessage().equals("timeout") || e.getMessage().equals("Read timed out"))) {
                        logger.error(e.getMessage());
                        e.printStackTrace();
                    }
                    for (String kbName : kbNameList) {
                        FileUtils.deleteFile(baseDir + kbName);
                        logger.debug(kbName + "删除成功");
                    }
                }

                @Override
                public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                    if (response.body() != null) {
                        String msg = JSONObject.parseObject(response.body().string()).getString("msg");
                        logger.debug(msg);
                    }
                    for (String kbName : kbNameList) {
                        FileUtils.deleteFile(baseDir + kbName);
                        logger.debug(kbName + "删除成功");
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public boolean post2Remote(String url, JSONObject jsonObject) {
        logger.info("请求大模型：" + url + jsonObject);
        try {
            RestTemplate restTemplate = new RestTemplate();

            // 创建请求头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);

            // 创建请求体
            Map<String, Object> body = new HashMap<>(jsonObject);

            // 创建 HttpEntity 对象
            HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, headers);

            // 发送 POST 请求并获取响应
            ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, entity, String.class);

            // 解析响应体
            org.springframework.boot.configurationprocessor.json.JSONObject response = new org.springframework.boot.configurationprocessor.json.JSONObject(responseEntity.getBody());
            logger.info(response.toString());
            if (response.getInt("code") == 200) {
                return true;
            } else {
                logger.error(response.getString("msg"));
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
            e.printStackTrace();
        }
        return false;
    }

}